/**
 *
 * \file CMessageBoxWindow.cpp
 * \brief This file contains the CMessageBoxWindow class implementation.
 * \author Vladimir Klubkov (aka marked)
 *
 * Last changes date:
 * \date 15.03.2011
 *
 * Copyright 2011 Vladimir Klubkov <marked-one@yandex.ru>
 *
 * This file is part of OOXlib.
 *
 * OOXLib is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * OOXlib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OOXlib; if not, you can try to find  it here:
 * http://www.gnu.org/licenses/gpl.txt
 * or write to the Free Software Foundation, Inc., 59 Temple Place,
 * Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "../include/CMessageBoxWindow.h"

namespace OOXlib {

/**
 * Constructor. Calls the base-class constructor.
 * \param File Source code file name.
 * \param iLine Source code line.
 * \param Function Function name.
 */
CMessageBoxWindow::CDeleteWindowException::CDeleteWindowException(const ::std::string &File, const int iLine, const ::std::string &Function)
: CException(File, iLine, Function, "Can't send the WM_DELETE_MESSAGE. The XSendEvent function failed.") {}
/**
 * Constructor.
 * \param pXServerConnection A pointer to the CXServerConnection object.
 * \param pScreen Pointer to the CScreen object.
 * \param Rectangle Window rectangle (contains coordinates of the left-top corner, width and height).
 * \param pEventHandler Pointer to the CEventHandler object.
 * \param WindowName Window name.
 * \param MessageText Text to draw.
 * \param IconPath ath to the icon pixmap file.
 * \param ImagePath Path to the image pixmap file.
 * \param pBackgroundColor Pointer to the CColor bject, which contains the background color.
 * \param pTextColor Pointer to the CColor bject, which contains the text color.
 * \param iHorizontalOffset Horizontal offset - default margins/free space betweeen elements.
 * \param iVerticalOffset Vertical offset - default margins/free space betweeen elements.
 */
CMessageBoxWindow::CMessageBoxWindow(const CXServerConnection *pXServerConnection, const CScreen *pScreen, const CUInt2dRectangle &Rectangle, const IEventHandler *pEventHandler,
                      const ::std::string &WindowName, const ::std::string &MessageText, const ::std::string &IconPath, const ::std::string &ImagePath,
                      const CColor *pBackgroundColor, const CColor *pTextColor, const unsigned int iHorizontalOffset, const unsigned int iVerticalOffset)
    : CSimpleWindow(pXServerConnection, pScreen, NULL, Rectangle, NoEventMask, NULL, WindowName, "MessageBox", IconPath, pBackgroundColor, 0, false, true),
    m_pGC(NULL), m_pImage(NULL), m_pButton(NULL), m_Text(MessageText), m_pTextColor(pTextColor), m_iTextOffset(iHorizontalOffset), m_iHorizontalOffset(iHorizontalOffset),
    m_iVerticalOffset(iVerticalOffset) {
        // Create graphics context.
        m_pGC = new CGraphicsContext(m_pXServerConnection, this);
        // Set text and compute text rectangle.
        m_pGC->SetText(m_Text);
        m_Rectangle = m_pGC->ComputeTextRectangle();
        // Get the image and compute the image rectangle.
        try {
            m_pImage = new CPixmap(m_pXServerConnection, m_pScreen, ImagePath);
        } catch(...) {
            m_pImage = NULL;
        }
        unsigned int imageWidth = 0u;
        unsigned int imageHeight = 0u;
        if(NULL != m_pImage) {
            imageWidth = m_pImage->GetWidth();
            imageHeight = m_pImage->GetHeight();
        }
        // Create the button and get the button size.
        CUInt2dRectangle rect(0, 0, 1, 1);
        m_pButton = new CButton(m_pXServerConnection, m_pScreen, this, rect, pEventHandler, "OK", pBackgroundColor, pTextColor, m_iHorizontalOffset, m_iVerticalOffset);
        unsigned int buttonWidth = m_pButton->GetSize().GetX();
        unsigned int buttonHeight = m_pButton->GetSize().GetY();
        // Compute the new window rectangle and the new button rectangle.
        unsigned int screenWidth = static_cast<unsigned int>(m_pScreen->ScreenWidth());
        unsigned int screenHeight = static_cast<unsigned int>(m_pScreen->ScreenHeight());
        unsigned int rectangleWidth = 0;
        if(0u == imageWidth) {
            rectangleWidth = m_Rectangle.GetWidth() + 2 * m_iHorizontalOffset;
        } else {
            rectangleWidth = m_Rectangle.GetWidth() + imageWidth + 3 * m_iHorizontalOffset;
            m_iTextOffset = 2 * m_iHorizontalOffset + imageWidth;
        }
        unsigned int rectangleHeight = 2 * m_iVerticalOffset;
        if(m_Rectangle.GetHeight() > imageHeight) {
            rectangleHeight += m_Rectangle.GetHeight();
        } else {
            rectangleHeight += imageHeight;
        }
        unsigned int buttonPosX = rectangleWidth / 2 - buttonWidth / 2;
        unsigned int buttonPosY = rectangleHeight + m_iVerticalOffset;
        rectangleHeight += 2 * m_iVerticalOffset + buttonHeight;
        unsigned int rectanglePosX = 0;
        unsigned int rectanglePosY = 0;
        if(rectangleWidth < screenWidth) {
            rectanglePosX = static_cast<unsigned int>(static_cast<double>(screenWidth) * 0.5) - static_cast<unsigned int>(static_cast<double>(rectangleWidth) * 0.5);
        }
        if(rectangleHeight < screenHeight) {
            rectanglePosY = static_cast<unsigned int>(static_cast<double>(screenHeight) * 0.5) - static_cast<unsigned int>(static_cast<double>(rectangleHeight) * 0.5);
        }
        m_Rectangle.Set(rectanglePosX, rectanglePosY, rectangleWidth, rectangleHeight);
        rect.Set(buttonPosX, buttonPosY, buttonWidth, buttonHeight);
        // Destroy button.
        delete m_pButton;
        // Destroy GC.
        delete m_pGC;
        // Destroy window.
        Destroy();
        // Create the new window.
        Create(pScreen, NULL, m_Rectangle, KeyPressMask | ExposureMask | ButtonPressMask | StructureNotifyMask, pEventHandler, WindowName, "MessageBox",
               IconPath, pBackgroundColor, 0, false, true);
               // Create new GC.
        m_pGC = new CGraphicsContext(m_pXServerConnection, this);
        // Set text.
        m_pGC->SetText(m_Text);
        // Create button.
        m_pButton = new CButton(m_pXServerConnection, m_pScreen, this, rect, pEventHandler, "OK", pBackgroundColor, pTextColor, m_iHorizontalOffset, m_iVerticalOffset);
    }
/**
 * Destructor.
 */
CMessageBoxWindow::~CMessageBoxWindow() {
    delete m_pButton;
    delete m_pGC;
    delete m_pImage;
}
/**
 * Exposure event handler.
 */
void CMessageBoxWindow::OnExpose() {
    if(NULL == m_pTextColor) {
        CColor color(m_pXServerConnection, m_pScreen, 255, 255, 255);
        m_pGC->SetForegroundColor(&color);
    } else {
        m_pGC->SetForegroundColor(m_pTextColor);
    }
    m_pGC->DrawText(CUInt2dPoint(m_iTextOffset, m_iVerticalOffset));
    if(NULL != m_pImage) {
        m_pGC->DrawPixmap(*m_pImage, CUInt2dPoint(m_iHorizontalOffset, m_iVerticalOffset));
    }
}
/**
 * If 'Esc', 'Return' or 'Space' key is pressed, sends the "WM_DELETE_WINDOW" message to the X Server.
 * \param iKey Key Id.
 */
void CMessageBoxWindow::OnKeyPress(::XID iKey) {
    if(XK_Escape == iKey || XK_Return == iKey || XK_space == iKey) {
        ::XEvent event;
        event.xclient.type = ClientMessage;
        event.xclient.window = m_Window;
        // XInternAtom can generate BadAlloc and BadValue errors.
        event.xclient.message_type = ::XInternAtom(m_pXServerConnection->Get(), "WM_PROTOCOLS", true);
        event.xclient.format = 32;
        // XInternAtom can generate BadAlloc and BadValue errors.
        event.xclient.data.l[0] = ::XInternAtom(m_pXServerConnection->Get(), "WM_DELETE_WINDOW", false);
        event.xclient.data.l[1] = CurrentTime;
        // XSendEvent can generate BadValue and BadWindow errors.
        if(0 == ::XSendEvent(m_pXServerConnection->Get(), m_Window, False, NoEventMask, &event)) {
            OOXLib_THROW(CDeleteWindowException);
        }
    }
}

} // OOXlib

